三年迭代复盘:企业级手机当扫码枪小程序在弱网环境下的容灾设计
记得是21年初,我们团队接了个挺有意思的活儿。一家做汽车零部件的厂商,嫌工业PDA扫码枪太贵,后续固件维护也麻烦,就问我们能不能用普通安卓手机,装个微信小程序,直接当扫码枪使,在仓库里搞入库、拣货和盘点。当时我作为技术侧负责人,心里还嘀咕,这不就是调个相机接口、解析一下条码嘛,能有多复杂?
三年一晃过去,这小程序前后迭代了十几个版本,扛住了零售、制造、物流等十来个企业客户的生产级流量。现在回头看,真正让我们脱胎换骨的,不是什么识别率优化,而是被弱网环境逼出来的那套容灾设计。今天趁项目阶段性上线,抽空掏心窝子跟各位复盘一下,也免得后来人踩同样的坑。
企业级场景和个人玩票完全不同。仓库里满是大铁架子,WiFi信号进了货架通道就跟进了迷宫似的,丢包、高延迟是家常便饭。手机还都是员工自己的,型号乱七八糟,系统版本也参差不齐。我们一开始真把事儿想简单了。
第一年:裸奔调用,被重复提交打脸
第一个生产版本,扫码就直接 wx.request 往服务端甩。超时设了10秒。仓库网络一抖,界面转圈圈然后弹个“网络异常”。拣货员哪管你后端收没收到,看没反应就再扫一次。结果服务端其实第一次就落库了,第二次又来一遍,造成重复入库。
有个周五晚上,客户盘点发现盘亏三百多件,运维直接把电话打到我手机上。我们查日志才发现是弱网下的重试风暴。那时候所谓的容灾就是加个 retry,但没做幂等。这哪是容灾,简直是制造脏数据。后来我们才明白,在企业WMS(仓储管理系统)对接里,任何客户端写入都必须假定网络是不可靠的。
第二年:本地队列,却掉进状态不一致的坑
痛定思痛,第二年我们搞了本地存储队列。扫码先写 wx.setStorageSync,界面秒回“成功”,后台慢慢同步。网络断了就囤着,通了再发。体验是顺滑了,但新麻烦来了。
有回客户仓断网四十分钟,本地囤了快两百条记录。恢复后一股脑灌给服务端WMS,系统瞬时承压,更致命的是,断网期间仓库主管在后台手工改了几个商品状态,手机端不知情,强行覆盖,导致单据对不上。我们才懂:光有离线队列不够,你得让前端在断网时也能“懂业务”,而不能只当传声筒。当时团队里为了“前端要不要放业务逻辑”吵了好几天,最后还是落到了本地轻量校验上。
第三年:Local-First 架构与冲突仲裁
到了今年,我们彻底重写了弱网模块,核心思路就两条:终端优先(Local-First)和后端仲裁。
技术上,每次扫码生成本地 UUID 加设备指纹,服务端拿这个做唯一索引,天然防重。哪怕前端因为超时重试一百次,后端只认一次。同时,我们把单据基础信息、商品主数据在联网时预同步到小程序侧的一个轻量存储里(类似 IndexedDB 封装),断网时扫码不仅在本地排队,还会做本地校验,比如“这条码是否属于当前拣货单”,不对就立刻震动报警,而不是等联网才报错。
网络恢复后的同步也不是无脑丢队列。我们加了心跳探测,先拉服务端版本号,做增量合并。遇到冲突,比如本地操作和后台人工修改打架,按既定规则(如后台人工优先级高)仲裁,全量留痕供追溯。
顺便提一句,小程序本身不是为高频离线业务设计的。它的 Storage 上限早期才10MB,后来提到200MB但也得精打细算。我们在本地队列做了滚动清理和压缩,只留必要字段。还有,小程序切后台很容易被微信杀掉,所以我们把关键队列的落盘放在扫码回调的同步代码里,绝不敢用异步延后。这些细碎的坑,文档可不会告诉你。
此外,给仓管员做了个隐蔽的长按“强制同步”键,信号临界点时手动触发,很土但管用。
一点实效数据
在家电仓客户那边,日均扫码22万次,跑满三个月,弱网造成的业务失败率从最早的5.3%压到0.02%,且零数据丢失。这背后是和超时、脏数据死磕的汗水。我们参考了MESA关于制造执行数据交互的一些规范,最终在终端实现了准实时的“最终一致性”。
做企业级小程序,别信“套壳网页”的鬼话。手机网络天生脆弱,把容灾骨架搭在终端,才是真赋能实业。如果各位也在做类似项目,记住:把弱网当常态,把丢数据当事故,你的架构才不会在客户产线上崩盘。
微信号:18581869297